package com.example.flower_shop.filter;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.SaTokenException;
import com.example.flower_shop.common.Result;
import com.example.flower_shop.exception.exception;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;

@RestControllerAdvice
@Slf4j
public class RespFilter implements ResponseBodyAdvice<Object> {

    @Resource
    private ObjectMapper objectMapper;

    /**
     * 统一异常处理 自定义异常
     *
     * @param e RunException
     * @return Result
     */
    @ExceptionHandler(value = exception.class)
    @ResponseStatus()
    public Result runException(exception e) {
        return Result.fail(e.getCode(), e.getMessage());
    }

    /**
     * 统一异常处理 认证异常
     *
     * @param e SaTokenException
     * @return Result
     */
    @ExceptionHandler(value = SaTokenException.class)
    @ResponseStatus()
    public Result saTokenException(SaTokenException e) {
        if (e instanceof NotLoginException) {
            return Result.fail(HttpStatus.UNAUTHORIZED.value(), "未登录");
        }
        if (e instanceof NotRoleException) {
            return Result.fail(HttpStatus.FORBIDDEN.value(), "无权限");
        }
        if (e instanceof NotPermissionException) {
            return Result.fail(HttpStatus.FORBIDDEN.value(), "无权限");
        }
        return Result.fail(e.getCode(), StringUtils.substringBefore(e.getMessage(), "："));
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus()
    public Result handleException(Exception e) {
        // 捕捉validation异常
        if (e instanceof BindException) {
            List<FieldError> fieldErrors = ((BindException) e).getFieldErrors();
            if (fieldErrors.isEmpty()) {
                return Result.fail(e.getMessage());
            }
            FieldError error = fieldErrors.get(0);
            try {
                // 判断是否是DateTimeFormat或其他格式错误 而不是validation注解的错误
                RuntimeException tmExp = error.unwrap(RuntimeException.class);
                // 获取转换异常
                if (tmExp.getCause() instanceof ConversionFailedException conversion) {
                    Class<?> target = conversion.getTargetType().getObjectType();
                    if (LocalDateTime.class.isAssignableFrom(target)
                            || LocalDate.class.isAssignableFrom(target)
                            || LocalTime.class.isAssignableFrom(target)) {
                        return Result.fail("时间或日期格式错误");
                    }
                    return Result.fail("【" + error.getField() + "】字段转换失败，请检查");
                }
            } catch (IllegalArgumentException illegal) {
                String message = error.getDefaultMessage();
                if (message == null || message.isEmpty()) {
                    return Result.fail("请检查【" + error.getField() + "】字段");
                }
                return Result.fail(message.substring(message.lastIndexOf("\n") + 1));
            }
        }
        if (e instanceof NoResourceFoundException) {
            return Result.fail(HttpStatus.NOT_FOUND.value(), "资源不存在");
        }
        e.printStackTrace(System.err);
        return Result.fail(HttpStatus.INTERNAL_SERVER_ERROR.name());
    }

    /**
     * 判断当前请求是否应该被特定的转换器处理。
     *
     * @param returnType    返回类型参数
     * @param converterType 转换器类型
     * @return 如果当前请求URI以"/api"开头，则返回true，否则返回false。
     * 如果请求属性为空，则返回true。
     */
    @Override
    public boolean supports(MethodParameter returnType,
                            Class<? extends HttpMessageConverter<?>> converterType) {
        // 如果不是项目的API接口，则不进行beforeBodyWrite的处理 以免影响swagger接口文档的正常显示
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = requestAttributes.getRequest();
            String requestURI = request.getRequestURI();
            return requestURI.startsWith("/api");
        }
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof org.springframework.core.io.Resource) {
            return body;
        }
        if (body instanceof Result) {
            return body;
        }
        if (body instanceof CharSequence) {
            try {
                return objectMapper.writeValueAsString(Result.success(body));
            } catch (JsonProcessingException e) {
                log.error("json序列化异常", e);
            }
        }
        return Result.success(body);
    }
}
